/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.data.provider.shp;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import chunmap.model.coord.Coordinate2D;
import chunmap.model.coord.Position;
import chunmap.model.elem.Envelope;
import chunmap.model.geom.*;

/**
 * @author chunquedong
 * 
 */
public class ShapefileReaderHelper {

	public Envelope readEnvelop(RandomAccessFile br) throws IOException {
		double xMin = reverseBytes(br.readDouble());
		double yMin = reverseBytes(br.readDouble());
		double xMax = reverseBytes(br.readDouble());
		double yMax = reverseBytes(br.readDouble());

		return new Envelope(xMin, yMin, xMax, yMax);
	}

	public double reverseBytes(double d) {
		long lon = Long.reverseBytes(Double.doubleToRawLongBits(d));
		return Double.longBitsToDouble(lon);
	}

	public Position readPoint(RandomAccessFile br) throws IOException {
		double x = reverseBytes(br.readDouble());
		double y = reverseBytes(br.readDouble());

		return new Coordinate2D(x, y);
	}

	public MultiPoint readMultiPoint(RandomAccessFile br) throws IOException {

		Envelope env = readEnvelop(br);
		int numPoints = Integer.reverseBytes(br.readInt());

		List<Position> points = readPointList(br, numPoints);
		
		List<GeoPoint> geoPointList=new ArrayList<GeoPoint>();
		for(Position p:points){
			geoPointList.add(new GeoPoint(p));
		}
		
		MultiPoint mPoint = new MultiPoint(geoPointList);

		mPoint._setEnvelop(env);
		return mPoint;
	}

	private List<Position> readPointList(RandomAccessFile br, int num)
			throws IOException {
		List<Position> points = new ArrayList<Position>();
		for (int i = 0; i < num; i++) {
			points.add(readPoint(br));
		}
		return points;
	}

	private LineString readLineString(RandomAccessFile br, int num)
			throws IOException {
		List<Position> points = readPointList(br, num);
		LineString ls = new LineString(points);
		return ls;
	}

	public MultiLineString readMultiLine(RandomAccessFile br)
			throws IOException {

		List<LineString> multiPath = new ArrayList<LineString>();
		Envelope env = readEnvelop(br);

		int numParts = Integer.reverseBytes(br.readInt());
		int numPoints = Integer.reverseBytes(br.readInt());

		// 几何体中第一个点在点集里面的索引，从零开始。
		int[] parts = new int[numParts];
		for (int i = 0; i < numParts; i++) {
			parts[i] = Integer.reverseBytes(br.readInt());
		}
		// 读取线串
		for (int i = 0; i < numParts; i++) {
			LineString ls;
			if (i == (numParts - 1)) {
				// 表示读最后一个几何体
				int last = numPoints;
				ls = readLineString(br, last - parts[i]);
			} else {
				ls = readLineString(br, parts[i + 1] - parts[i]);
			}
			multiPath.add(ls);
		}
		MultiLineString mls = new MultiLineString(multiPath);
		mls._setEnvelop(env);
		return mls;
	}

	// public MultiLineString readMultiLineZ(RandomAccessFile br)
	// throws IOException {
	// return readMultiLine(br);
	// }
	//
	// public MultiLineString readMultiLineM(RandomAccessFile br)
	// throws IOException {
	// return readMultiLine(br);
	// }

	// 读取多边形
	public MultiPolygon readMultiPolygon(RandomAccessFile br)
			throws IOException {

		List<Polygon> polygons = new ArrayList<Polygon>();
		Envelope env = readEnvelop(br);

		int numParts = Integer.reverseBytes(br.readInt());
		int numPoints = Integer.reverseBytes(br.readInt());
		// 几何体中第一个点在点集里面的索引，从零开始。
		int[] parts = new int[numParts];
		for (int i = 0; i < numParts; i++) {
			parts[i] = Integer.reverseBytes(br.readInt());
		}

		// 读取环
		for (int i = 0; i < numParts; i++) {
			Ring r;
			if (i == (numParts - 1)) {
				// 表示读最后一个几何体
				int last = numPoints;
				r = readLineString(br, last - parts[i]).toLinearRing();
			} else {
				r = readLineString(br, parts[i + 1] - parts[i]).toLinearRing();
			}

			Polygon pg = new Polygon(r);
			polygons.add(pg);
		}
		MultiPolygon mpolygon = new MultiPolygon(polygons);
		mpolygon._setEnvelop(env);
		return mpolygon;
	}

}